/*
 *   SlimPlayer Copyright (c) 2006 Koelec
 *
 *   This file is part of SlimPlayer plugin for SageTv.
 *
 *   Slimplayer is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   Slimplayer is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with SoftSqueeze; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
package nl.cmkprojects.sage.slimplayer;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import nl.cmkprojects.sage.slimplayer.squeeze.CliMessage;
import nl.cmkprojects.sage.slimplayer.squeeze.CliParameterIterator;

import org.apache.log4j.Logger;

/**
 * @author Richard Titmuss
 * 
 */
public class CliCon {
	private final static Logger logger = Logger.getLogger(CliCon.class);

	private final static int DISCONNECTED = 0;

	private final static int CONNECTED = 1;

	private Socket socket;

	private BufferedReader socketIn;

	private PrintWriter socketOut;

	private int inState = DISCONNECTED;

	private String playerid;

	private boolean isSqueezeCenter;

	private int listSize;

	private String slimserver;

	private int webport;

	public CliCon(String playerid, boolean isSqueezeCenter, int listSize,
			String slimserver, int webport) {
		this.playerid = playerid;
		this.isSqueezeCenter = isSqueezeCenter;
		this.listSize = listSize;
		this.slimserver = slimserver;
		this.webport = webport;
	}

	public void loadPlaylistById(String id) {
		// String cmd = "playlistcontrol cmd:load playlist_id:" + id;
		String cmd = "playlist clear";
		CliMessage msg = new CliMessage(playerid, cmd);
		sendMessage(msg);
		cmd = "playlistcontrol cmd:add playlist_id:" + id;
		msg = new CliMessage(playerid, cmd);
		sendMessage(msg);
	}

	public void stopPlayback() {
		CliMessage cmd = new CliMessage(playerid, "stop");
		sendMessage(cmd);
	}

	public void startPlayback() {
		CliMessage cmd = new CliMessage(playerid, "play");
		sendMessage(cmd);
	}

	public void deleteTrack(String playlistId, String index) {
		CliMessage cmd = new CliMessage(playerid, "playlists edit playlist_id:"
				+ playlistId + " cmd:delete index:" + index);
		sendMessage(cmd);
	}

	public void deletePlaylistById(String id) {
		CliMessage cmd = new CliMessage(playerid,
				"playlists delete playlist_id:" + id);
		sendMessage(cmd);
	}

	public void deleteFavoriteById(String id) {
		CliMessage cmd = new CliMessage(playerid, "favorites delete " + id);
		sendMessage(cmd);
	}

	public void delete(Playable reference) {
		logger.debug("delete " + reference);
		if (reference instanceof Playlist) {
			Playlist pl = (Playlist) reference;
			deletePlaylistById(pl.getId());
		} else if (reference instanceof Favorite) {
			Favorite f = (Favorite) reference;
			deleteFavoriteById(f.getId());
		} else if (reference instanceof Track) {
			Track t = (Track) reference;
			deleteTrack(t.getPlaylistId(), t.getPlaylistIndex());
		}
	}

	public void playUrl(String url) {
		CliMessage cmd = new CliMessage(playerid, "playlist play " + url);
		sendMessage(cmd);
	}

	public void playTrack(String index) {
		CliMessage cmd = new CliMessage(playerid, "playlist index " + index);
		sendMessage(cmd);
	}

	public void playFavorite(Favorite f) {
		CliMessage cmd = new CliMessage(playerid,
				"favorites playlist play item_id:" + f.getId());
		sendMessage(cmd);
	}

	public void play(Playable reference) {
		logger.debug("play " + reference);
		if (reference instanceof Playlist) {
			Playlist pl = (Playlist) reference;
			loadPlaylistById(pl.getId());
			playTrack("0");
		} else if (reference instanceof Favorite) {
			Favorite f = (Favorite) reference;
			if (isSqueezeCenter) {
				playFavorite(f);
			} else {
				playUrl(f.getUrl());
			}
		} else if (reference instanceof Track) {
			Track t = (Track) reference;
			loadPlaylistById(t.getPlaylistId());
			playTrack(t.getPlaylistIndex());
		}
	}

	public Favorite[] getFavorites() {
		List<Favorite> list = new ArrayList<Favorite>(listSize);
		String cmd = "favorites 0 " + listSize;
		if (isSqueezeCenter) {
			cmd = "favorites items 0 " + listSize + " want_url:1";
		}
		CliMessage msg = new CliMessage(playerid, cmd);
		CliMessage reply = sendMessage(msg);
		if (reply != null) {
			if (reply.getCommand() != null
					&& reply.getCommand().equalsIgnoreCase("favorites")) {
				CliParameterIterator iter = reply.getParameterIterator();
				if (isSqueezeCenter) {
					if (iter.hasNext()) {
						iter.nextParameter(); // items
					}
				}
				if (iter.hasNext()) {
					iter.nextParameter(); // 0
				}
				if (iter.hasNext()) {
					iter.nextParameter(); // LISTSIZE
				}
				if (iter.hasNext()) {
					iter.nextTag(); // count tag
					iter.nextValue(); // 
				}
				if (isSqueezeCenter) {
					if (iter.hasNext()) {
						iter.nextTag(); // want_url
						iter.nextValue(); // 
					}
				}
				String url = "";
				String id = "";
				String name = "";
				while (iter.hasNext()) {
					String tag = iter.nextTag();
					if (isSqueezeCenter) {
						if (tag.equals("name")) {
							name = Util.urlDecode(iter.nextValue());
						}
					} else {
						if (tag.equals("title")) {
							name = Util.urlDecode(iter.nextValue());
						}
					}
					if (tag.equals("id")) {
						id = iter.nextValue();
					}
					if (tag.equals("url")) {
						url = Util.urlDecode(iter.nextValue());
						list.add(new FavoriteImpl(name, id, url));
					}
				}
			}
			return (Favorite[]) list.toArray(new Favorite[0]);
		}
		return null;
	}

	public Playlist[] getPlaylists() {
		List<Playlist> list = new ArrayList<Playlist>(listSize);
		String cmd = "playlists 0 " + listSize;
		CliMessage msg = new CliMessage(playerid, cmd);
		CliMessage reply = sendMessage(msg);
		if (reply != null) {
			if (reply.getCommand() != null
					&& reply.getCommand().equalsIgnoreCase("playlists")) {
				CliParameterIterator iter = reply.getParameterIterator();
				if (iter.hasNext()) {
					iter.nextParameter(); // 0
				}
				if (iter.hasNext()) {
					iter.nextParameter(); // 100
				}
				String id = "";
				String name = "";
				while (iter.hasNext()) {
					String tag = iter.nextTag();
					if (tag.equals("id")) {
						id = iter.nextValue();
					}
					if (tag.equals("playlist")) {
						name = Util.urlDecode(iter.nextValue());
						list.add(new PlaylistImpl(name, id));
					}
				}
			}
		}
		return (Playlist[]) list.toArray(new Playlist[0]);
	}

	public Track[] getTracks(Playlist playlist) {
		List<Track> list = new ArrayList<Track>(listSize);
		if (playlist != null) {
			list.add((new TrackImpl("..", "", "", playlist.getId())));
			if (playlist.getId() != null && playlist.getId().length() > 0) {
				String cmd = "playlists tracks 0 " + listSize + " playlist_id:"
						+ playlist.getId();
				CliMessage msg = new CliMessage(playerid, cmd);
				CliMessage reply = sendMessage(msg);
				if (reply != null) {
					if (reply.getCommand() != null
							&& reply.getCommand().equalsIgnoreCase("playlists")) {
						CliParameterIterator iter = reply
								.getParameterIterator();
						String tag = "";
						if (iter.hasNext()) {
							tag = iter.nextParameter(); // tracks
						}
						if (iter.hasNext()) {
							tag = iter.nextParameter(); // 0
						}
						if (iter.hasNext()) {
							tag = iter.nextParameter(); // 256
						}
						if (iter.hasNext()) {
							tag = iter.nextParameter(); // playlistid:
						}
						String index = "";
						String id = "";
						String name = "";
						while (iter.hasNext()) {
							tag = iter.nextTag();
							if (tag.equals("playlist index")) {
								index = iter.nextValue();
							}
							if (tag.equals("id")) {
								id = iter.nextValue();
							}
							if (tag.equals("title")) {
								name = Util.urlDecode(iter.nextValue());
								list.add(new TrackImpl(name, id, index,
										playlist.getId()));
							}
						}
					}
				}
			}
		}
		return (Track[]) list.toArray(new Track[0]);
	}

	public boolean isRemoteStream() {
		CliMessage cmd = new CliMessage(playerid, "remote ?");
		CliMessage reply = sendMessage(cmd);
		if (reply != null) {
			if (reply.getCommand() != null
					&& reply.getCommand().equalsIgnoreCase("remote")) {
				CliParameterIterator iter = reply.getParameterIterator();
				if (iter.hasNext()) {
					String value = iter.nextParameter();
					if (value.equalsIgnoreCase("0")) {
						return false;
					} else if (value.equalsIgnoreCase("1")) {
						return true;
					}
				}
			}
		}
		return false;

	}

	public String getCurrentStation() {
		if (isRemoteStream()) {
			CliMessage cmd = new CliMessage(playerid, "title ?");
			CliMessage reply = sendMessage(cmd);
			if (reply != null) {
				if (reply.getCommand() != null
						&& reply.getCommand().equalsIgnoreCase("title")) {
					CliParameterIterator iter = reply.getParameterIterator();
					if (iter.hasNext()) {
						String station = Util.urlDecode(iter.nextParameter());
						// if (station.startsWith("tunein-station.pls"))
						// {
						// if (currentStationKey != null
						// && station.equals(currentStationKey))
						// {
						// station = currentStationValue;
						// } else
						// {
						// // try to find station in favorites url list
						// Favorite[] favorites = getFavorites();
						// for (int i = 0; i < favorites.length; i++)
						// {
						// if (favorites[i].getUrl().indexOf(station) != -1)
						// {
						// currentStationValue = favorites[i].getName();
						// currentStationKey = station;
						// return currentStationValue;
						// }
						// }
						// }
						// }
						return station;
					}
				}
			}
		}
		return "";
	}

	public String getArtist() {
		String artist = "";
		CliMessage cmd = new CliMessage(playerid, "artist ?");
		CliMessage reply = sendMessage(cmd);
		if (reply != null) {
			if (reply.getCommand() != null
					&& reply.getCommand().equalsIgnoreCase("artist")) {
				CliParameterIterator iter = reply.getParameterIterator();
				if (iter.hasNext()) {
					artist = iter.nextParameter();
				}
			}
		}
		return artist;
	}

	public boolean isPlaying() {
		CliMessage cmd = new CliMessage(playerid, "mode ?");
		CliMessage reply = sendMessage(cmd);
		if (reply != null) {
			if (reply.getCommand() != null
					&& reply.getCommand().equalsIgnoreCase("mode")) {
				CliParameterIterator iter = reply.getParameterIterator();
				if (iter.hasNext()) {
					if (iter.nextParameter().equalsIgnoreCase("play")) {
						return true;
					}
				}
			}
		}
		return false;
	}

	public void ir(String code, int jiffies) {
		CliMessage cmd = new CliMessage(playerid, "ir " + code + " " + jiffies);
		sendMessage(cmd);
	}

	public void button(String button) {
		CliMessage cmd = new CliMessage(playerid, "button " + button);
		sendMessage(cmd);
	}

	public boolean isMuted() {
		CliMessage cmd = new CliMessage(playerid, "mixer muting ?");
		CliMessage reply = sendMessage(cmd);
		if (reply != null) {
			if (reply.getCommand() != null
					&& reply.getCommand().equalsIgnoreCase("mixer")) {
				CliParameterIterator iter = reply.getParameterIterator();
				if (iter.hasNext()) {
					iter.next(); // muting
				}
				if (iter.hasNext()) {
					String value = iter.nextParameter();
					if (value.equalsIgnoreCase("0")) {
						return false;
					} else if (value.equalsIgnoreCase("1")) {
						return true;
					}
				}
			}
		}
		return false;
	}

	public void setVolume(int volume) {
		CliMessage cmd = new CliMessage(playerid, "mixer volume " + volume);
		sendMessage(cmd);
	}

	public String getDisplayText() {
		String text = "";
		CliMessage cmd = new CliMessage(playerid, "display ?");
		CliMessage reply = sendMessage(cmd);
		if (reply != null) {
			StringBuffer sb = new StringBuffer();
			if (reply.getCommand() != null
					&& reply.getCommand().equalsIgnoreCase("display")) {
				CliParameterIterator iter = reply.getParameterIterator();
				while (iter.hasNext()) {
					sb.append(iter.nextParameter());
					if (iter.hasNext()) {
						sb.append("\n");
					}
				}
			}
			text = sb.toString();
		}
		return text;
	}

	public void mute(boolean value) {
		CliMessage cmd = new CliMessage(playerid, "mixer muting "
				+ (value ? "1" : "0"));
		sendMessage(cmd);
	}

	public void muteToggle() {
		CliMessage cmd = new CliMessage(playerid, "mixer muting");
		sendMessage(cmd);
	}

	public String getCurrentTitle() {
		String title = "";
		CliMessage cmd = new CliMessage(playerid, "current_title ?");
		CliMessage reply = sendMessage(cmd);
		if (reply != null) {
			if (reply.getCommand() != null
					&& reply.getCommand().equalsIgnoreCase("current_title")) {
				CliParameterIterator iter = reply.getParameterIterator();
				if (iter.hasNext()) {
					title = iter.nextParameter();
				}
			}
		}
		return title;
	}

	public String getTitle() {
		String title = "";
		CliMessage cmd = new CliMessage(playerid, "title ?");
		CliMessage reply = sendMessage(cmd);
		if (reply != null) {
			if (reply.getCommand() != null
					&& reply.getCommand().equalsIgnoreCase("title")) {
				CliParameterIterator iter = reply.getParameterIterator();
				if (iter.hasNext()) {
					title = iter.nextParameter();
				}
			}
		}
		return title;
	}

	public boolean isPowerOn() {
		CliMessage cmd = new CliMessage(playerid, "power ?");
		CliMessage reply = sendMessage(cmd);
		if (reply != null) {
			if (reply.getCommand() != null
					&& reply.getCommand().equalsIgnoreCase("power")) {
				CliParameterIterator iter = reply.getParameterIterator();
				if (iter.hasNext()) {
					String value = iter.nextParameter();
					if (value.equalsIgnoreCase("0")) {
						return false;
					} else if (value.equalsIgnoreCase("1")) {
						return true;
					}
				}
			}
		}
		return false;
	}

	public void logon(String username, String password) {
		CliMessage cmd = new CliMessage(playerid, "login " + username + " "
				+ password);
		sendMessage(cmd);
	}

	public void powerOn() {
		CliMessage cmd = new CliMessage(playerid, "power 1");
		sendMessage(cmd);
	}

	public void powerOff() {
		CliMessage cmd = new CliMessage(playerid, "power 0");
		sendMessage(cmd);
	}

	public String getPath() {
		CliMessage cmd = new CliMessage(playerid, "path ?");
		CliMessage reply = sendMessage(cmd);
		if (reply != null) {
			if (reply.getCommand() != null
					&& reply.getCommand().equalsIgnoreCase("path")) {
				CliParameterIterator iter = reply.getParameterIterator();
				if (iter.hasNext()) {
					return iter.nextParameter();
				}
			}
		}
		return "";
	}

	public Map<String, String> getSongInfo(String selTag, String tagVal) {
		logger.debug("getSongInfo(" + selTag + "," + tagVal + ")");
		Map<String, String> song = new HashMap<String, String>();
		// song.put("CoverartUrl",
		// "http://localhost:9000/music/blabla/cover.jpg");
		song.put("CoverartUrl", "");
		CliMessage cmd = new CliMessage(playerid, "songinfo 0 100 " + selTag
				+ ":" + tagVal);
		CliMessage reply = sendMessage(cmd);
		if (reply != null) {
			if (reply.getCommand() != null
					&& reply.getCommand().equalsIgnoreCase("songinfo")) {
				CliParameterIterator iter = reply.getParameterIterator();
				if (iter.hasNext()) {
					iter.next(); // 0
				}
				if (iter.hasNext()) {
					iter.next(); // 100
				}

				while (iter.hasNext()) {
					String tag = iter.nextTag();
					String val = iter.nextValue();

					if (tag.equals("id"))
						song.put("Id", val);
					else if (tag.equals("title"))
						song.put("Title", val);
					else if (tag.equals("genre"))
						song.put("Genre", val);
					else if (tag.equals("genre_id"))
						song.put("Genre_id", val);
					else if (tag.equals("artist"))
						song.put("Artist", val);
					else if (tag.equals("artist_id"))
						song.put("Artist_id", val);
					else if (tag.equals("composer"))
						song.put("Composer", val);
					else if (tag.equals("band"))
						song.put("Band", val);
					else if (tag.equals("album"))
						song.put("Album", val);
					else if (tag.equals("album_id"))
						song.put("Album_id", val);
					else if (tag.equals("duration")) {
						if (val != null && val.length() > 0) {
							song.put("Duration",
									SageTVApi.DurFormat(new Double(val)
											.longValue() * 1000));
						}
					} else if (tag.equals("disc"))
						song.put("Disc", val);
					else if (tag.equals("disccount"))
						song.put("Disccount", val);
					else if (tag.equals("tracknum"))
						song.put("Tracknum", val);
					else if (tag.equals("year"))
						song.put("Year", val);
					else if (tag.equals("bpm"))
						song.put("Bpm", val);
					else if (tag.equals("drm"))
						song.put("Drm", val);
					else if (tag.equals("comment"))
						song.put("Comment", val);
					else if (tag.equals("lyrics"))
						song.put("Lyrics", val);
					else if (tag.equals("filesize"))
						song.put("Filesize", val);
					else if (tag.equals("conductor"))
						song.put("Conductor", val);
					else if (tag.equals("type"))
						song.put("Type", val);
					else if (tag.equals("modificationTime"))
						song.put("ModificationTime", val);
					else if (tag.equals("bitrate"))
						song.put("Bitrate", val);
					else if (tag.equals("coverart")) {
						if (val.equals("1")) {
							song.put("CoverartUrl",
									getCoverArtUrl((String) song.get("Id")));
						}
						// else
						// {
						// song.put("CoverartUrl",
						// "http://localhost:9000/music/blabla/cover.jpg");
						// }
						song.put("Coverart", val.equals("1") ? "Yes" : "No");
					} else if (tag.equals("coverartThumb"))
						song.put("CoverartThumb", val.equals("1") ? "Yes"
								: "No");
					else if (tag.equals("url"))
						song.put("Url", val);
				}
			}
		}
		return song;
	}

	private String getCoverArtUrl(String id) {
		return "http://chris:koele@" + slimserver + ":" + webport + "/music/"
				+ id + "/cover.jpg";
	}

	/**
	 * Connect the cli to the slimserver at the given address and port.
	 * 
	 * @param addr
	 * @param port
	 */
	public void connect(InetAddress addr, int port) throws IOException {
		disconnect();

		socket = new Socket(addr, port);
		socket.setSoTimeout(60000);
		socketIn = new BufferedReader(new InputStreamReader(socket
				.getInputStream(), "UTF-8"));
		socketOut = new PrintWriter(socket.getOutputStream());
		inState = CONNECTED;
	}

	/**
	 * Disconnect the cli from the slimserver.
	 */
	public void disconnect() throws IOException {
		if (!isConnected())
			return;

		inState = DISCONNECTED;
		socket.close();
	}

	/**
	 * @return true if the cli is connected to the slimserver
	 */
	public boolean isConnected() {
		return inState == CONNECTED;
	}

	public CliMessage sendMessage(CliMessage message) {
		if (isConnected()) {
			if (logger.isDebugEnabled())
				logger.debug("send cli msg: " + message);
			socketOut.print(message + "\n");
			socketOut.flush();
			return readMessage();
		}
		return null;
	}

	private CliMessage readMessage() {
		try {
			String line = socketIn.readLine();
			if (line == null)
				return null;

			logger.debug("got reply " + line);
			return CliMessage.parseMessage(line);
		} catch (Exception e) {
			e.printStackTrace();
			try {
				disconnect();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}
		return null;
	}
}
